﻿/*!
 * Copyright (c), 上海沃恩信息科技有限公司.
 * All rights reserved.
 * Licensed under BSD (https://www.pittypat.work/bsd.html).
 */

using System;

namespace Pittypat
{
    /// <summary>
    /// 表示一天中的时间刻度，精确到毫秒，24 小时制。
    /// </summary>
    public struct @time : IPrimitive
    {
        /// <summary>
        /// 将指定的字符串解析为一个 @time 值。
        /// </summary>
        /// <param name="s">要解析的字符串。</param>
        /// <returns>一个 @time 或 null。</returns>
        public static @time? Parse(string s)
        {
            if (String.IsNullOrEmpty(s))
            {
                return null;
            }

            s = s.Trim();
            if (String.IsNullOrEmpty(s))
            {
                return null;
            }

            switch (s.ToLower())
            {
                case "min":
                case "minvalue":
                case "min_value":
                case "0":
                case "zero":
                case "default":
                    return MinValue;
                case "max":
                case "maxvalue":
                case "max_value":
                    return MaxValue;
                case "now":
                case "current":
                    return Now;
                default:
                    break;
            }
            
            int v = 0;
            if (int.TryParse(s, out v))
            {
                return new time(v);
            }

            DateTime t = DateTime.MinValue;
            if (DateTime.TryParse(s, out t))
            {
                return new time(t.Hour, t.Minute, t.Second, t.Millisecond);
            }

            try
            {
                var a = s.Split(':', '.', '/', '\\', '|');
                int hour = 0;
                int minute = 0;
                int second = 0;
                int ms = 0;

                if (a.Length > 0)
                {
                    hour = int.Parse(a[0]);
                }

                if (a.Length > 1)
                {
                    minute = int.Parse(a[1]);
                }

                if (a.Length > 2)
                {
                    second = int.Parse(a[2]);
                }

                if (a.Length > 3)
                {
                    ms = int.Parse(a[3]);
                }

                return new @time(hour, minute, second, ms);
            }
            catch
            {
                return null;
            }
        }

        /// <summary>
        /// 获取当前时间。
        /// </summary>
        public static @time Now
        {
            get
            {
                return DateTime.Now;
            }
        }

        /// <summary>
        /// 比较两个值的大小。
        /// </summary>
        /// <param name="x">第一个值。</param>
        /// <param name="y">第二个值。</param>
        /// <returns>如果 x 大于 y，则返回 true，否则返回 false。</returns>
        public static bool operator >(@time x, @time y)
        {
            return x.value > y.value;
        }

        /// <summary>
        /// 比较两个值的大小。
        /// </summary>
        /// <param name="x">第一个值。</param>
        /// <param name="y">第二个值。</param>
        /// <returns>如果 x 大于等于 y，则返回 true，否则返回 false。</returns>
        public static bool operator >=(@time x, @time y)
        {
            return x.value >= y.value;
        }

        /// <summary>
        /// 比较两个值的大小。
        /// </summary>
        /// <param name="x">第一个值。</param>
        /// <param name="y">第二个值。</param>
        /// <returns>如果 x 小于 y，则返回 true，否则返回 false。</returns>
        public static bool operator <(@time x, @time y)
        {
            return x.value < y.value;
        }

        /// <summary>
        /// 比较两个值的大小。
        /// </summary>
        /// <param name="x">第一个值。</param>
        /// <param name="y">第二个值。</param>
        /// <returns>如果 x 小于等于 y，则返回 true，否则返回 false。</returns>
        public static bool operator <=(@time x, @time y)
        {
            return x.value <= y.value;
        }

        /// <summary>
        /// 表示 00:00:00。
        /// </summary>
        public static readonly @time MinValue = new @time(0, 0, 0);
        /// <summary>
        /// 表示 23:59:59:999。
        /// </summary>
        public static readonly @time MaxValue = new @time(23, 59, 59, 999);

        private int value;

        /// <summary>
        /// 初始化 @time 结构的新实例。
        /// </summary>
        /// <param name="hour">小时。</param>
        /// <param name="minute">分钟。</param>
        /// <param name="second">秒。</param>
        /// <param name="millisecond">毫秒。</param>
        public @time(int hour, int minute, int second, int millisecond = 0)
        {
            hour = hour < 0 ? 0 : (hour > 24 ? 24 : hour);
            minute = minute < 0 ? 0 : (minute > 59 ? 59 : minute);
            second = second < 0 ? 0 : (second > 59 ? 59 : second);
            millisecond = millisecond < 0 ? 0 : (millisecond > 999 ? 999 : millisecond);

            this.value = hour * 10000000 + minute * 100000 + second * 1000 + millisecond;
        }

        /// <summary>
        /// 初始化 @time 结构的新实例。
        /// </summary>
        /// <param name="value">结构的值。</param>
        internal @time(int value)
        {
            int hour = value / 10000000;
            int minute = (value / 100000) % 100;
            int second = (value / 1000) % 100;
            int millisecond = value % 1000;

            hour = hour < 0 ? 0 : (hour > 24 ? 24 : hour);
            minute = minute < 0 ? 0 : (minute > 59 ? 59 : minute);
            second = second < 0 ? 0 : (second > 59 ? 59 : second);
            millisecond = millisecond < 0 ? 0 : (millisecond > 999 ? 999 : millisecond);

            this.value = hour * 10000000 + minute * 100000 + second * 1000 + millisecond;
        }

        /// <summary>
        /// 获取小时。
        /// </summary>
        public int Hour
        {
            get
            {
                return this.value / 10000000;
            }
        }

        /// <summary>
        /// 获取分钟。
        /// </summary>
        public int Minute
        {
            get
            {
                return (this.value / 100000) % 100;
            }
        }

        /// <summary>
        /// 获取秒数。
        /// </summary>
        public int Second
        {
            get
            {
                return (this.value / 1000) % 100;
            }
        }

        /// <summary>
        /// 获取毫秒。
        /// </summary>
        public int Millisecond
        {
            get
            {
                return this.value % 1000;
            }
        }

        /// <summary>
        /// 将当前值转换为 TimeSpan 类型的值。
        /// </summary>
        /// <param name="value">要转换的值。</param>
        /// <returns>转换后的值。</returns>
        public static explicit operator TimeSpan(@time value)
        {
            return new TimeSpan(0, value.Hour, value.Minute, value.Second, value.Millisecond);
        }

        /// <summary>
        /// 将指定的值转换为 @time 类型的值。
        /// </summary>
        /// <param name="value">要转换的值。</param>
        /// <returns>转换后的值。</returns>
        public static explicit operator @time(TimeSpan value)
        {
            return new time(value.Hours, value.Minutes, value.Seconds, value.Milliseconds);
        }

        /// <summary>
        /// 将当前值转换为 int 类型的值。
        /// </summary>
        /// <param name="value">要转换的值。</param>
        /// <returns>转换后的值。</returns>
        public static implicit operator int(@time value)
        {
            return value.value;
        }

        /// <summary>
        /// 将指定的值转换为 @time 类型的值。
        /// </summary>
        /// <param name="value">要转换的值。</param>
        /// <returns>转换后的值。</returns>
        public static implicit operator @time(int value)
        {
            return new @time(value);
        }

        /// <summary>
        /// 将指定的值转换为 @time 类型的值。
        /// </summary>
        /// <param name="value">要转换的值。</param>
        /// <returns>转换后的值。</returns>
        public static implicit operator @time(string value)
        {
            return Parse(value) ?? MinValue;
        }

        /// <summary>
        /// 将指定的值转换为 @time 类型的值。
        /// </summary>
        /// <param name="value">要转换的值。</param>
        /// <returns>转换后的值。</returns>
        public static implicit operator @time(DateTime value)
        {
            return new time(value.Hour, value.Minute, value.Second, value.Millisecond);
        }

        /// <summary>
        /// 比较两个值是否相等。
        /// </summary>
        /// <param name="x">第一个值。</param>
        /// <param name="y">第二个值。</param>
        /// <returns>如果两个值相等，则返回 true，否则返回 false。</returns>
        public static bool operator==(@time x, @time y)
        {
            return x.value == y.value;
        }

        /// <summary>
        /// 比较两个值是否不相等。
        /// </summary>
        /// <param name="x">第一个值。</param>
        /// <param name="y">第二个值。</param>
        /// <returns>如果两个值不相等，则返回 true，否则返回 false。</returns>
        public static bool operator !=(@time x, @time y)
        {
            return x.value != y.value;
        }

        /// <summary>
        /// 判断两个值是否相等。
        /// </summary>
        /// <param name="obj">要比较的值。</param>
        /// <returns>如果两个值相等，则返回 true，否则返回 false。</returns>
        public override bool Equals(object obj)
        {
            if (obj is @time)
            {
                return this.value == ((@time)obj).value;
            }

            unchecked
            {
                if (obj is sbyte)
                {
                    return this.value == (sbyte)obj;
                }

                if (obj is byte)
                {
                    return this.value == (byte)obj;
                }

                if (obj is short)
                {
                    return this.value == (short)obj;
                }

                if (obj is ushort)
                {
                    return this.value == (ushort)obj;
                }

                if (obj is int)
                {
                    return this.value == (int)obj;
                }

                if (obj is uint)
                {
                    return this.value == (uint)obj;
                }

                if (obj is long)
                {
                    return this.value == (long)obj;
                }

                if (obj is ulong)
                {
                    return (ulong)this.value == (ulong)obj;
                }

                if (obj is decimal)
                {
                    return this.value == (decimal)obj;
                }

                if (obj is float)
                {
                    return this.value == (float)obj;
                }

                if (obj is double)
                {
                    return this.value == (double)obj;
                }
            }

            return false;
        }

        /// <summary>
        /// 返回此值的哈希代码。
        /// </summary>
        /// <returns>此值的哈希代码。</returns>
        public override int GetHashCode()
        {
            return this.value;
        }

        /// <summary>
        /// 返回此值的字符串表示形式。
        /// </summary>
        /// <returns>此值的字符串表示形式。</returns>
        public override string ToString()
        {
            return this.ToString(null);
        }

        #region IPrimitive Members

        object IPrimitive.Value
        {
            get
            {
                return this.value;
            }
        }

        /// <summary>
        /// 将当前对象作为 JSON 字符串写入指定的文本流。
        /// </summary>
        /// <param name="writer">目标文本流。</param>
        /// <exception cref="ArgumentNullException">writer 是 null。</exception>
        public void Write(System.IO.TextWriter writer)
        {
            if (writer == null)
            {
                throw new ArgumentNullException("writer");
            }

            writer.Write("{{\"hour\":{0},\"minute\":{1},\"second\":{2},\"millisecond\":{3}}}",
                this.Hour, this.Minute, this.Second, this.Millisecond);
        }

        #endregion

        #region IConvertible Members

        TypeCode IConvertible.GetTypeCode()
        {
            return TypeCode.Object;
        }

        bool IConvertible.ToBoolean(IFormatProvider provider)
        {
            return this.value != 0;
        }

        byte IConvertible.ToByte(IFormatProvider provider)
        {
            return unchecked((byte)this.value);
        }

        char IConvertible.ToChar(IFormatProvider provider)
        {
            return unchecked((char)this.value);
        }

        DateTime IConvertible.ToDateTime(IFormatProvider provider)
        {
            DateTime now = DateTime.Now;
            return new DateTime(now.Year, now.Month, now.Day, this.Hour, this.Minute, this.Second, this.Millisecond);
        }

        decimal IConvertible.ToDecimal(IFormatProvider provider)
        {
            return unchecked((decimal)this.value);
        }

        double IConvertible.ToDouble(IFormatProvider provider)
        {
            return unchecked((double)this.value);
        }

        short IConvertible.ToInt16(IFormatProvider provider)
        {
            return unchecked((short)this.value);
        }

        int IConvertible.ToInt32(IFormatProvider provider)
        {
            return unchecked((int)this.value);
        }

        long IConvertible.ToInt64(IFormatProvider provider)
        {
            return unchecked((long)this.value);
        }

        sbyte IConvertible.ToSByte(IFormatProvider provider)
        {
            return unchecked((sbyte)this.value);
        }

        float IConvertible.ToSingle(IFormatProvider provider)
        {
            return unchecked((float)this.value);
        }

        string IConvertible.ToString(IFormatProvider provider)
        {
            return this.value.ToString(provider);
        }

        object IConvertible.ToType(Type conversionType, IFormatProvider provider)
        {
            return ((IConvertible)this.value).ToType(conversionType, provider);
        }

        ushort IConvertible.ToUInt16(IFormatProvider provider)
        {
            return unchecked((ushort)this.value);
        }

        uint IConvertible.ToUInt32(IFormatProvider provider)
        {
            return unchecked((uint)this.value);
        }

        ulong IConvertible.ToUInt64(IFormatProvider provider)
        {
            return unchecked((ulong)this.value);
        }

        #endregion

        #region IComparable Members

        int IComparable.CompareTo(object other)
        {
            if (other is @time)
            {
                return this.value.CompareTo(((@time)other).value);
            }

            return 1;
        }

        #endregion

        #region IFormattable Members

        /// <summary>
        /// 使用指定的格式格式化当前实例的值。
        /// </summary>
        /// <param name="format">指定要使用的格式的 String。</param>
        /// <param name="formatProvider">用于格式化该值的 IFormatProvider。null 表示从操作系统的当前区域设置中获取数字格式信息。</param>
        /// <returns>一个 String，包含具有指定格式的当前实例的值。</returns>
        public string ToString(string format, IFormatProvider formatProvider)
        {
            DateTime today = DateTime.Today;
            return (new DateTime(today.Year, today.Month, today.Day, this.Hour, this.Minute, this.Second, this.Millisecond)).ToString(format ?? "HH:mm:ss", formatProvider);
        }

        /// <summary>
        /// 使用指定的格式格式化当前实例的值。
        /// </summary>
        /// <param name="format">指定要使用的格式的 String。</param>
        /// <returns>一个 String，包含具有指定格式的当前实例的值。</returns>
        public string ToString(string format)
        {
            return this.ToString(format, null);
        }

        #endregion
    }
}
